package boltdb

import (
	"encoding/json"
	"sync"

	"github.com/VolantMQ/persistence"
	"github.com/boltdb/bolt"
)

const (
	bucketTopicsList = "topicslist"
)

type auth struct {
	db *dbStatus

	wgTx *sync.WaitGroup
	lock *sync.Mutex
}

func (a *auth) AddUser(creds *persistence.AuthCreds) (string, error) {
	var index []byte

	err := a.db.db.Update(func(tx *bolt.Tx) error {
		authDB := tx.Bucket(bucketAuth)
		if authDB == nil {
			return persistence.ErrNotInitialized
		}

		id, err := authDB.NextSequence()
		if err != nil {
			return err
		}

		index = itob64(id)
		buck, err := authDB.CreateBucket(index)
		if err != nil {
			return err
		}

		if e := buck.Put([]byte("user"), []byte(creds.User)); e != nil {
			return e
		}
		if e := buck.Put([]byte("password"), []byte(creds.Password)); e != nil {
			return e
		}

		list, err := json.Marshal(creds.Topics)
		if err != nil {
			return err
		}

		if e := buck.Put([]byte("list"), list); e != nil {
			return e
		}

		return nil
	})

	return string(index), err
}

func (a *auth) DelUser(id string) error {

	err := a.db.db.Update(func(tx *bolt.Tx) error {
		authDB := tx.Bucket(bucketAuth)
		if authDB == nil {
			return persistence.ErrNotInitialized
		}

		err := authDB.DeleteBucket([]byte(id))
		if err != nil {
			return err
		}
		return nil
	})

	return err
}

func (a *auth) ListUsers() ([]*persistence.AuthCreds, error) {
	auths := make([]*persistence.AuthCreds, 0)

	err := a.db.db.View(func(tx *bolt.Tx) error {
		authDB := tx.Bucket(bucketAuth)
		if authDB == nil {
			return persistence.ErrNotInitialized
		}

		return authDB.ForEach(func(k, v []byte) error { // nolint: errcheck
			creds := &persistence.AuthCreds{Topics: make([]persistence.TopicsNode, 0)}
			if item := authDB.Bucket(k); item != nil {
				creds.ID = string(k)

				if data := item.Get([]byte("user")); data != nil {
					creds.User = string(data)
				} else {
					return persistence.ErrBrokenEntry
				}
				if data := item.Get([]byte("password")); data != nil {
					creds.Password = string(data)
				} else {
					return persistence.ErrBrokenEntry
				}
				if data := item.Get([]byte("list")); data != nil {
					err := json.Unmarshal(data, &creds.Topics)
					if err != nil {
						return err
					}
				} else {
					return persistence.ErrBrokenEntry
				}

				auths = append(auths, creds)
			} else {
				return persistence.ErrNotFound
			}
			return nil
		})
	})

	if err != nil {
		return nil, err
	}

	return auths, nil
}
